#include "../dish.h"
#include "../chef.h"
#include "test_utilities.h"
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>

#define ASSERT_NULL(a) ASSERT_TEST((a)==NULL)
#define ASSERT_EQUAL(a,b) ASSERT_TEST((a)==(b))
#define ASSERT_STRING_EQUAL(a,b) ASSERT_TEST(strcmp((a),(b))==0)
#define ASSERT_SUCCESS(a) ASSERT_TEST((a)==CHEF_SUCCESS)
#define ASSERT_INVALID(a) ASSERT_TEST((a)==CHEF_INVALID_ARGUMENT)
#define ASSERT_NO_DISHES(a) ASSERT_TEST((a)==CHEF_NO_DISHES)

typedef struct {
	Dish dish;
	Chef chef;
} Examples;

static Examples setUp() {
	Examples examples;
	examples.dish = dishCreate("D1",ENTREE,10,10,10,NULL);
	examples.chef = chefCreate("MOSHE", NULL);
	chefAddDish(examples.chef, examples.dish, 2, NULL);
	return examples;
}

void tearDown(Examples examples) {
	chefDestroy(examples.chef);
	dishDestroy(examples.dish);
}

static bool testChefCreate() {
	ChefResult result;
	Chef chef=chefCreate(NULL, &result);
	ASSERT_INVALID(result);
	ASSERT_NULL(chef);
	chef=chefCreate("MOSHE", &result);
	ASSERT_TEST(chef);
	ASSERT_SUCCESS(result);
	ASSERT_STRING_EQUAL(chefGetName(chef),"MOSHE");
	Chef chef2=chefCreate("MOSHE", NULL);
	ASSERT_TEST(chef2);
	chefDestroy(chef);
	chefDestroy(chef2);
	return true;
}

static bool testChefCopy() {
	ChefResult result;
	SET_UP(Examples);
	Chef chef=chefCopy(NULL, &result);
	ASSERT_INVALID(result);
	ASSERT_NULL(chef);
	chef=chefCopy(examples.chef, &result);
	ASSERT_TEST(chef);
	ASSERT_SUCCESS(result);
	ASSERT_STRING_EQUAL(chefGetName(chef),"MOSHE");
	Chef chef2=chefCopy(examples.chef, NULL);
	ASSERT_TEST(chef2);
	chefDestroy(chef);
	chefDestroy(chef2);
	TEAR_DOWN();
	return true;
}

static bool testChefDestroy() {
	chefDestroy(NULL);
	Chef chef=chefCreate("MOSHE", NULL);
	chefDestroy(chef);
	return true;
}

static bool testChefGetName() {
	SET_UP(Examples);
	const char* name=chefGetName(NULL);
	ASSERT_NULL(name);
	name=chefGetName(examples.chef);
	ASSERT_STRING_EQUAL(name,"MOSHE");
	TEAR_DOWN();
	return true;
}

static bool testChefGetScore() {
	SET_UP(Examples);
	int score=chefGetScore(NULL);
	ASSERT_EQUAL(score,-1);
	score=chefGetScore(examples.chef);
	ASSERT_EQUAL(score,0);
	chefIncrementScore(examples.chef);
	score=chefGetScore(examples.chef);
	ASSERT_EQUAL(score,1);
	TEAR_DOWN();
	return true;
}

static bool testChefIncrementScore() {
	SET_UP(Examples);
	chefIncrementScore(NULL);
	int currentScore=chefGetScore(examples.chef);
	chefIncrementScore(examples.chef);
	ASSERT_EQUAL(chefGetScore(examples.chef),currentScore+1);
	TEAR_DOWN();
	return true;
}

static bool testChefAddDish() {
	ChefResult result;
	SET_UP(Examples);
	chefAddDish(NULL, examples.dish, 0, &result);
	ASSERT_INVALID(result);
	chefAddDish(examples.chef, NULL, 0, &result);
	ASSERT_INVALID(result);
	chefAddDish(examples.chef, examples.dish, -1, &result);
	ASSERT_INVALID(result);
	chefAddDish(examples.chef, examples.dish, 0, &result);
	ASSERT_SUCCESS(result);
	Dish dish1=chefGetFavoriteDish(examples.chef, NULL);
	ASSERT_TEST(dish1);
	Dish dish2=chefGetFavoriteDish(examples.chef, NULL);
	ASSERT_TEST(dish2);
	ASSERT_STRING_EQUAL(dishGetName(dish1),dishGetName(dish2));
	ASSERT_EQUAL(dish1,dish2);
	chefAddDish(examples.chef, examples.dish, 0, NULL);
	TEAR_DOWN();
	return true;
}

static bool testChefGetFavoriteDish() {
	ChefResult result;
	SET_UP(Examples);
	Dish dish1 = chefGetFavoriteDish(NULL, &result);
	ASSERT_INVALID(result);
	ASSERT_NULL(dish1);
	Chef chef=chefCreate("HAIM", NULL);
	dish1 = chefGetFavoriteDish(chef, &result);
	ASSERT_NO_DISHES(result);
	ASSERT_NULL(dish1);
	dish1 = dishCreate("D2",ENTREE,10,10,10,NULL);
	chefAddDish(examples.chef, dish1, 5, NULL);
	Dish dish2 = chefGetFavoriteDish(examples.chef, &result);
	ASSERT_SUCCESS(result);
	ASSERT_STRING_EQUAL(dishGetName(dish1),dishGetName(dish2));
	chefGetFavoriteDish(examples.chef, NULL);
	dishDestroy(dish1);
	chefDestroy(chef);
	TEAR_DOWN();
	return true;
}

static bool testChefRemoveFavoriteDish() {
	ChefResult result;
	SET_UP(Examples);
	chefRemoveFavoriteDish(NULL, &result);
	ASSERT_INVALID(result);
	chefRemoveFavoriteDish(examples.chef, &result);
	ASSERT_SUCCESS(result);
	chefRemoveFavoriteDish(examples.chef, &result);
	ASSERT_NO_DISHES(result);
	TEAR_DOWN();
	return true;
}

int main() {
	RUN_TEST(testChefCreate);
	RUN_TEST(testChefCopy);
	RUN_TEST(testChefDestroy);
	RUN_TEST(testChefGetName);
	RUN_TEST(testChefGetScore);
	RUN_TEST(testChefIncrementScore);
	RUN_TEST(testChefAddDish);
	RUN_TEST(testChefGetFavoriteDish);
	RUN_TEST(testChefRemoveFavoriteDish);
	return 0;
}
